#include "libcomm.h"
#if (COMM_FEATURE_REGEX==1)

#ifdef COMM_MSVC
	#define PCRE_STATIC

	#ifdef COMM_X64
	#pragma comment(lib,"../../depend/pcre/build/x64/release/pcre.lib")
	#pragma comment(lib,"../../depend/pcre/build/x64/release/pcreposix.lib")
	#else
	#pragma comment(lib,"../../depend/pcre/build/win32/release/pcre.lib")
	#pragma comment(lib,"../../depend/pcre/build/win32/release/pcreposix.lib")
	#endif
#endif
#include "../depend/pcre/pcreposix.h"

class RegexImp
{
public:
	// flags: '.' can match any character include newline char '\n' while have not REG_NEWLINE.
	RegexImp(const char* content, const char* pattern, int flags=(REG_EXTENDED|REG_NEWLINE))
		:m_content(content),m_nmatch(1),m_matchptr(NULL),m_offset(0)
	{
		MYLIB_ASSERT_NOTNULL(content);
		MYLIB_ASSERT_NOTNULL(pattern);
		const char* pos = pattern;
		while(pos[0] != 0)
		{
			if(pos[0] == '(')
				m_nmatch++;
			pos++;
		}
		m_matchptr = (regmatch_t*)Buffer::malloc(sizeof(regmatch_t)*m_nmatch);
		MYLIB_ASSERT_NOTNULL(m_matchptr);

		MYLIB_ASSERT(0 == regcomp(&m_reg,pattern,flags));

	}

	~RegexImp()
	{ 
		regfree(&m_reg); 
		if(NULL != m_matchptr)
		{
			Buffer::free(m_matchptr);
			m_matchptr = NULL;
		}
	}

	// do it while return true for matching continue.
	bool Match(StringList& groupList)
	{
		groupList.clear();
		while(m_offset < m_content.length())
		{
			int errcode;
			Buffer errmsg(512);
			String matched;

			errcode = regexec(&m_reg,(char*)m_content + m_offset,m_nmatch,m_matchptr,0);
			if(0 != errcode)
			{
				if(REG_NOMATCH != errcode)
				{
					regerror(errcode,&m_reg,errmsg,errmsg.size());
					LOGWARN("%s error: %s.",__FUNCTION__,(char*)errmsg);
				}
				break;
			}

			for (int i=0;i<m_nmatch;i++)
			{
				if(m_matchptr[i].rm_so == -1)
					continue;

				matched.resize(m_matchptr[i].rm_eo - m_matchptr[i].rm_so + 1);
				strncpy((char*)matched,(char*)m_content + m_offset + m_matchptr[i].rm_so, matched.size()-1);
				matched[matched.size()-1] = 0;
				//LOGNOTICE("%d Regex Matched: %s.",i,(char*)matched);	

				groupList.append(matched);
			}

			m_offset += m_matchptr[0].rm_eo;
			return true;
		}

		return false;
	}

private:
	regex_t m_reg;
	int m_nmatch;
	regmatch_t* m_matchptr;
	String m_content;
	int m_offset;
};

//////////////////////////////////////////////////////////////////////////

#define REGEXINST	((RegexImp*)m_inst)

Regex::Regex(const char* content, const char* pattern, bool dotCanMatchNewLine):m_inst(NULL)
{
	m_inst = new RegexImp(content,pattern,dotCanMatchNewLine?REG_EXTENDED:(REG_EXTENDED|REG_NEWLINE));
	MYLIB_ASSERT_NOTNULL(m_inst);
}

Regex::~Regex()
{
	if(NULL != m_inst)
	{
		delete REGEXINST;
		m_inst = NULL;
	}
}

bool Regex::Match(StringList& groupList)
{
	return REGEXINST->Match(groupList);
}

#endif